package com.biz.primus.ms.payment.service;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.biz.primus.base.enums.PaymentStatusEnum;
import com.biz.primus.base.exception.BizSilentException;
import com.biz.primus.base.exception.MethodNotSupportException;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.common.utils.CopyUtils;
import com.biz.primus.common.utils.DateUtil;
import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.model.payment.enums.AllInPayTrxStatusCode;
import com.biz.primus.model.payment.enums.PaymentExceptionEnum;
import com.biz.primus.model.payment.enums.PaymentType;
import com.biz.primus.model.payment.vo.req.*;
import com.biz.primus.model.payment.vo.resp.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.payment.channel.alipay.IAlipayPayment;
import com.biz.primus.ms.payment.channel.alipay.config.AlipayConfig;
import com.biz.primus.ms.payment.channel.allinpay.lang.RSA;
import com.biz.primus.ms.payment.channel.allinpay.res.AllInPayH5PayRespVo;
import com.biz.primus.ms.payment.channel.allinpay.res.AllInPayPayRespVo;
import com.biz.primus.ms.payment.channel.allinpay.res.AllInPayScanPayRespVo;
import com.biz.primus.ms.payment.channel.wechat.exceptions.PaymentException;
import com.biz.primus.ms.payment.channel.wechat.exceptions.SignIncorrectException;
import com.biz.primus.ms.payment.channel.wechat.res.WechatPayNotifyRespVo;
import com.biz.primus.ms.payment.dao.po.PaymentOrder;
import com.biz.primus.ms.payment.exception.PaymentOrderIsExpiredException;
import com.biz.primus.ms.payment.exception.PaymentOrderIsPaidException;
import com.biz.primus.ms.payment.trans.PaymentReqVoToPaymentOrderReqVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

import static com.biz.primus.model.payment.enums.PaymentExceptionEnum.ILLEGAL_PARAMETER;
import static com.biz.primus.model.payment.enums.PaymentExceptionEnum.PAYMENT_ORDER_IS_EXPIRED;
import static com.biz.primus.ms.payment.channel.alipay.IAlipayPayment.ParamKeys.*;
import static java.lang.String.format;

/**
 * 为商户订单提供支付相关服务
 * Created by defei on 9/27/17.
 */
@Slf4j
@Service
public class PaymentServiceImpl extends AbstractBaseService implements PaymentService {


    @Value("${primus.tl.cer}")
    private String CER_PATH;

    @Value("${primus.tl.pfx}")
    private String PFX_PATH;

    @Value("${primus.tl.pwd}")
    private String TL_PWD;

    @Value("${primus.tl.alias}")
    private String TL_ALIAS;

    private static final String SUCCESS = "success";
    private final MerchantService merchantService;

    private final PaymentChannelService paymentChannelService;

    private final PaymentOrderService paymentOrderService;

    private final AliPaymentService aliPaymentService;

    private final WechatPaymentService wechatPaymentService;

    private final WechatPaymentLogService wechatPaymentLogService;

    private final AlipayPaymentLogService alipayPaymentLogService;

    private final AllInPayPaymentLogService allInPayPaymentLogService;

    private final AllInPayPaymentService allInPayPaymenService;

    @Autowired
    public PaymentServiceImpl(MerchantService merchantService,
                              PaymentChannelService paymentChannelService,
                              PaymentOrderService paymentOrderService,
                              AliPaymentService aliPaymentService,
                              WechatPaymentService wechatPaymentService,
                              WechatPaymentLogService wechatPaymentLogService,
                              AlipayPaymentLogService alipayPaymentLogService,
                              AllInPayPaymentLogService allinpayPaymentLogService,
                              AllInPayPaymentService allinpayPaymenService) {
        this.merchantService = merchantService;
        this.paymentChannelService = paymentChannelService;
        this.paymentOrderService = paymentOrderService;
        this.aliPaymentService = aliPaymentService;
        this.wechatPaymentService = wechatPaymentService;
        this.wechatPaymentLogService = wechatPaymentLogService;
        this.alipayPaymentLogService = alipayPaymentLogService;
        this.allInPayPaymentLogService = allinpayPaymentLogService;
        this.allInPayPaymenService = allinpayPaymenService;
    }

    /**
     * 创建支付单，有到第三方支付进行统一下单及签名
     *
     * @param paymentReqVo {@link PaymentReqVo}
     * @return {@link PaymentRespVo}
     * @throws PaymentOrderIsPaidException 如果该笔订单已经支付过
     */
    @Override
    public PaymentRespVo createPayment(PaymentReqVo paymentReqVo) throws PaymentOrderIsPaidException, PaymentOrderIsExpiredException, PaymentException {

        log.info("Create PaymentOrder for {}", paymentReqVo);
        AssertUtils.notNull(paymentReqVo, ILLEGAL_PARAMETER, "Argument paymentReqVo is null");
        AssertUtils.notNull(paymentReqVo.getExpireTime(), ILLEGAL_PARAMETER, "Argument paymentReqVo.expireTime is null");

        if (paymentReqVo.getExpireTime().getTime() < System.currentTimeMillis()) {
            throw new PaymentOrderIsExpiredException(PAYMENT_ORDER_IS_EXPIRED, "Order is expired.");
        }
        PaymentChannelResVo paymentChannel = paymentChannelService.getPaymentChannelInfo(paymentReqVo.getMchId(), paymentReqVo.getPaymentType(), paymentReqVo.getTradeType());
        validateExistPaymentOrderPaymentStatusAndDiscardNotPaid(paymentReqVo.getSeller(), paymentReqVo.getMchOrderNo());
        PaymentOrder paymentOrder = paymentOrderService.create(new PaymentReqVoToPaymentOrderReqVo(paymentReqVo.getSeller()).apply(paymentReqVo));
        Object paymentSdkParameters;
        Object payInfo = null;
        switch (paymentReqVo.getPaymentType()) {
            case ALIPAY:
                AliOrderReqVo aliPaymentReqVo = JsonUtils.json2Obj(paymentChannel.getParam(), AliOrderReqVo.class);
                switch (paymentReqVo.getTradeType()) {
                    case ALIPAY_MOBILE:
                        aliPaymentReqVo.setService(IAlipayPayment.Service.MOBILE_SECURITY_PAY);
                        paymentSdkParameters = aliPaymentService.createMobilePayment(paymentReqVo, aliPaymentReqVo);
                        break;
                    case ALIPAY_PC:
                        aliPaymentReqVo.setService(IAlipayPayment.Service.CREATE_DIRECT_PAY_BY_USER);
                        paymentSdkParameters = aliPaymentService.createPcPayment(paymentReqVo, aliPaymentReqVo);
                        break;
                    case ALIPAY_WAP:
                        aliPaymentReqVo.setService(IAlipayPayment.Service.WAP_DIRECT_PAY);
                        paymentSdkParameters = aliPaymentService.createWapPayment(paymentReqVo, aliPaymentReqVo);
                        break;
                    default:
                        throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentTradeType is null or illegal.");
                }
                break;
            case WECHAT:
                WechatOrderReqVo wechatPaymentReqVo = new WechatOrderReqVo();
                wechatPaymentReqVo.setTradeType(paymentReqVo.getTradeType());
                AssertUtils.notNull(wechatPaymentReqVo.getWechatTradeType(), ILLEGAL_PARAMETER, format("Unsupported wechat trade type:%s", paymentReqVo.getTradeType()));
                Properties properties = JsonUtils.json2Obj(paymentChannel.getParam(), Properties.class);
                paymentSdkParameters = wechatPaymentService.createPayment(properties, wechatPaymentReqVo, paymentReqVo);
                break;
            case ALL_IN_PAY_JS_API:
            case ALL_IN_PAY:
                AllInPayPayRespVo payment = allInPayPaymenService.createPayment(paymentReqVo);
                payInfo = payment.getPayinfo();
                paymentSdkParameters = payment;
                break;
            case ALL_IN_PAY_CARD:
                paymentSdkParameters = allInPayPaymenService.createPaymentCard(paymentReqVo);
                break;
            case ALL_IN_SCAN_PAY:
                AllInPayScanPayRespVo paymentScan = allInPayPaymenService.createPaymentScan(paymentReqVo);
                payInfo = paymentScan.getPayinfo();
                paymentSdkParameters = paymentScan;
                break;
            default:
                throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentType is null or illegal.");
        }

        PaymentRespVo paymentRespVo = new PaymentRespVo();
        paymentRespVo.setId(paymentOrder.getId());
        paymentRespVo.setMchOrderNo(paymentReqVo.getMchOrderNo());
        paymentRespVo.setPayInfo(JsonUtils.obj2Json(payInfo));
        paymentRespVo.setPaymentSdkParametersJson(JsonUtils.obj2Json(paymentSdkParameters));
        return paymentRespVo;
    }

    /**
     * 通联撤销支付
     * 只能撤销当天的交易，全额退款，实时返回退款结果
     *
     * @param reqVo 通联撤销支付请求
     * @return PaymentCancelResp
     */
    public PaymentCancelResp cancelPayment(PaymentCancelReqVo reqVo) {
        PaymentCancelResp cancelRespVo;
        AssertUtils.notNull(reqVo, ILLEGAL_PARAMETER, "Argument paymentCancelReqVo is null");
        PaymentOrder paymentOrder = paymentOrderService.findLatestPaymentOrder(reqVo.getOrderNo());
        PaymentType paymentType = reqVo.getPaymentType();
        switch (paymentType) {
            case ALL_IN_PAY:
            case ALL_IN_PAY_JS_API:
            case ALL_IN_SCAN_PAY:
                cancelRespVo = allInPayPaymenService.cancel(reqVo);
                break;
            default:
                throw new PaymentException("不支持的取消的交易方式");
        }
        AllInPayTrxStatusCode trxStatus = cancelRespVo.getTrxStatus();
        switch (trxStatus) {
            case SUCCESS:
                paymentOrder.setPaymentStatus(PaymentStatusEnum.REFUNDED);
                paymentOrder.setPaySuccessTime(DateUtil.now());
                break;
            case PROCESSING:
                paymentOrder.setPaymentStatus(PaymentStatusEnum.REFUNDING);
                break;
            default:
                break;
        }
        paymentOrderService.update(paymentOrder);
        return cancelRespVo;
    }

    /**
     * 检查商户订单支付状态
     *
     * @param paymentQueryVo {@link PaymentQueryReqVo}
     * @return {@link PaymentQueryRespVo}
     * @throws BizSilentException 如果订单不存在
     */
    @Override
    public PaymentQueryRespVo checkPaymentStatus(PaymentQueryReqVo paymentQueryVo) throws BizSilentException {
        log.info("Check payment status for {}", paymentQueryVo);
        AssertUtils.notNull(paymentQueryVo, ILLEGAL_PARAMETER, "Argument paymentReqVo is null");

        PaymentOrder paymentOrder;
        if (paymentQueryVo.getId() == null) {
            paymentOrder = paymentOrderService.findLatestPaymentOrder(paymentQueryVo.getMchId().toString(), paymentQueryVo.getMchOrderNo());
        } else {
            paymentOrder = paymentOrderService.findOne(paymentQueryVo);
        }

        if (paymentOrder.getPaymentStatus() == PaymentStatusEnum.CREATE_PAYMENT) {
            CommonPaidRespVo commonPaidRespVo = isPaid(paymentOrder);
            if (commonPaidRespVo.isPaid()) {
                paymentOrder.setPaymentStatus(PaymentStatusEnum.PAID);
                confirmPaid(paymentOrder.getId(), commonPaidRespVo.getTradeNo());
            }
        }

        PaymentQueryRespVo paymentQueryRespVo = new PaymentQueryRespVo();
        CopyUtils.copyProperties(paymentQueryVo, paymentQueryRespVo);
        CopyUtils.copyProperties(paymentOrder, paymentQueryRespVo);
        return paymentQueryRespVo;
    }

    @Override
    public PaymentRefundRespVo refund(PaymentRefundReqVo reqVo) throws PaymentException {

        log.info("Refund payment for {}", reqVo);
        AssertUtils.notNull(reqVo, ILLEGAL_PARAMETER, "Argument reqVo is null");
        PaymentQueryRespVo paymentQueryRespVo = checkPaymentStatus(reqVo);
        PaymentRefundRespVo paymentRefundRespVo;
        if (paymentQueryRespVo.getPaymentStatus() == PaymentStatusEnum.REFUNDED) {
            return buildRefundSuccessRespVo();
        }
        if (paymentQueryRespVo.getPaymentStatus() == PaymentStatusEnum.PAID) {
            PaymentChannelResVo paymentChannelInfo = paymentChannelService.getPaymentChannelInfo(paymentQueryRespVo.getMchId(), paymentQueryRespVo.getPaymentType(), paymentQueryRespVo.getPaymentTradeType());

            switch (paymentQueryRespVo.getPaymentType()) {
                case ALIPAY:
                    AliOrderReqVo aliOrderReqVo = JsonUtils.json2Obj(paymentChannelInfo.getParam(), AliOrderReqVo.class);
                    paymentRefundRespVo = aliPaymentService.refund(reqVo, aliOrderReqVo);
                    break;
                case WECHAT:
                    Properties properties = JsonUtils.json2Obj(paymentChannelInfo.getParam(), Properties.class);
                    paymentRefundRespVo = wechatPaymentService.refund(properties, reqVo);
                    break;
                case ALL_IN_PAY_JS_API:
                case ALL_IN_PAY:
                case ALL_IN_SCAN_PAY:
                    paymentRefundRespVo = allInPayPaymenService.refund(reqVo);
                    break;
                case ALL_IN_PAY_CARD:
                    paymentRefundRespVo = allInPayPaymenService.refundCard(reqVo);
                    break;
                default:
                    throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentType is null or illegal.");
            }
            paymentRefundRespVo.setOutRefundNo(reqVo.getRefundNo());
            updateRefundStatus(paymentQueryRespVo.getId(), paymentRefundRespVo);
            return paymentRefundRespVo;
        } else {
            throw new BizSilentException(PaymentExceptionEnum.PAYMENT_ORDER_IS_NOT_PAID, "Order not paid.");
        }
    }

    /**
     * 退款查询
     *
     * @param reqVo {@link PaymentRefundReqVo}
     * @throws PaymentException
     */
    @Override
    public PaymentRefundRespVo refundQuery(PaymentRefundReqVo reqVo) throws PaymentException {
        PaymentQueryRespVo paymentQueryRespVo = checkPaymentStatus(reqVo);
        PaymentRefundRespVo paymentRefundRespVo;
        switch (paymentQueryRespVo.getPaymentStatus()) {
            case REFUNDED:
                return buildRefundSuccessRespVo();
            case REFUNDING:
                PaymentChannelResVo paymentChannelInfo = paymentChannelService.getPaymentChannelInfo(reqVo.getMchId(), paymentQueryRespVo.getPaymentType(), paymentQueryRespVo.getPaymentTradeType());
                switch (paymentQueryRespVo.getPaymentType()) {
                    case ALIPAY:
                        AliOrderReqVo aliOrderReqVo = JsonUtils.json2Obj(paymentChannelInfo.getParam(), AliOrderReqVo.class);
                        paymentRefundRespVo = aliPaymentService.refundQuery(reqVo, aliOrderReqVo);
                        break;
                    case WECHAT:
                        Properties properties = JsonUtils.json2Obj(paymentChannelInfo.getParam(), Properties.class);
                        paymentRefundRespVo = wechatPaymentService.refundQuery(properties, reqVo);
                        break;
                    case ALL_IN_PAY:
                    case ALL_IN_PAY_JS_API:
                    case ALL_IN_SCAN_PAY:
                        paymentRefundRespVo = allInPayPaymenService.refundQuery(reqVo);
                        break;
                    case ALL_IN_PAY_CARD:
                        paymentRefundRespVo = allInPayPaymenService.refundCardQuery(reqVo);
                        break;
                    default:
                        throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentType is null or illegal.");
                }
                break;
            default:
                throw new BizSilentException(PaymentExceptionEnum.PAYMENT_ORDER_IS_NOT_REFUND, "Order not refund.");
        }
        paymentRefundRespVo.setOutRefundNo(reqVo.getRefundNo());
        updateRefundStatus(paymentQueryRespVo.getId(), paymentRefundRespVo);
        return paymentRefundRespVo;
    }

    /**
     * 关闭第三方支付单
     *
     * @param paymentQueryVo {@link PaymentQueryReqVo}
     * @throws PaymentOrderIsPaidException 如果已经有支付单完成付款
     */
    @Override
    public void closePayment(PaymentQueryReqVo paymentQueryVo) throws PaymentOrderIsPaidException {

        log.info("Close payment for {}", paymentQueryVo);
        validateExistPaymentOrderPaymentStatusAndDiscardNotPaid(paymentQueryVo.getMchId().toString(), paymentQueryVo.getMchOrderNo());
    }


    @Override
    public void handlePaymentNotify(PaymentNotifyReqVo reqVo) throws SignIncorrectException {

        AssertUtils.notNull(reqVo, ILLEGAL_PARAMETER, "Argument reqVo is null");
        AssertUtils.notNull(reqVo.getPaymentType(), ILLEGAL_PARAMETER, "Argument reqVo.paymentType is null");
        AssertUtils.notNull(reqVo.getPaymentNotifyType(), ILLEGAL_PARAMETER, "Argument reqVo.paymentNotifyType is null");
        AssertUtils.isTrue(StringUtils.isNotBlank(reqVo.getNotifyData()), ILLEGAL_PARAMETER, "Argument reqVo.notifyData is null");
        PaymentOrder paymentOrder;
        switch (reqVo.getPaymentType()) {
            case ALIPAY:
                HashMap<String, String> notifyParams = aliPaymentService.parsePayNotifyRespVo(reqVo.getNotifyData());
                String mchId = notifyParams.get(PASSBACK_PARAMS);
                String mchOrderNo = notifyParams.get(OUT_TRADE_NO);
                switch (reqVo.getPaymentNotifyType()) {
                    case PAY:
                        paymentOrder = paymentOrderService.findLatestPaymentOrder(mchId, mchOrderNo);
                        alipayPaymentLogService.logWechatPaymentNotify(notifyParams, paymentOrder.getId());
                        if (IAlipayPayment.TradeStatus.TRADE_SUCCESS.equals(notifyParams.get(TRADE_STATUS))) {
                            confirmPaid(paymentOrder.getId(), notifyParams.get(TRADE_NO));
                        }
                        break;
                    case REFUND:
                        try {
                            paymentOrder = paymentOrderService.findLatestPaymentOrder(mchId, mchOrderNo);
                            boolean isTrue = AlipaySignature.rsaCheckV1(notifyParams, AlipayConfig.ali_public_key, AlipayConfig.input_charset, AlipayConfig.sign_type);
                            if (isTrue) {
                                confirmRefunded(paymentOrder.getId(), notifyParams.get(TRADE_NO));
                                return;
                            }
                        } catch (AlipayApiException e) {
                            log.info("Got an exception when alipay sing validate.", e);
                        }
                        throw new BizSilentException(ILLEGAL_PARAMETER, "Sing is incorrect.");
                    default:
                        throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentNotifyType is null or illegal.");
                }
            case WECHAT:
                switch (reqVo.getPaymentNotifyType()) {
                    case PAY:
                        WechatPayNotifyRespVo wechatPayNotifyRespVo = wechatPaymentService.parsePayNotifyRespVo(reqVo.getNotifyData());
                        paymentOrder = paymentOrderService.findLatestPaymentOrder(wechatPayNotifyRespVo.getAttach(), wechatPayNotifyRespVo.getOutTradeNo());
                        PaymentChannelResVo paymentChannelInfo = paymentChannelService.getPaymentChannelInfo(Long.valueOf(paymentOrder.getMchId()), paymentOrder.getPaymentType(), paymentOrder.getPaymentTradeType());
                        Properties properties = JsonUtils.json2Obj(paymentChannelInfo.getParam(), Properties.class);
                        wechatPayNotifyRespVo.getProperties().putAll(properties);
                        wechatPayNotifyRespVo.signValidate();
                        wechatPaymentLogService.logWechatPaymentNotify(wechatPayNotifyRespVo, paymentOrder.getId());
                        confirmPaid(paymentOrder.getId(), wechatPayNotifyRespVo.getTradeNo());
                        break;
                    case REFUND:
                        //TODO https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_16&index=10
                        throw new MethodNotSupportException();
                    default:
                        throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentNotifyType is null or illegal.");
                }
                break;
            default:
                throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentType is null or illegal.");
        }
    }

    /**
     * 通联h5支付结果
     */
    @Override
    public void handleAllInPayH5Notify(AllInPayH5PayRespVo reqVo) {
        String signMsg = reqVo.getSignMsg();
        String encrypt = RSA.encrypt(PFX_PATH, TL_PWD, TL_ALIAS, reqVo.getSignParams(), reqVo);
        if (!Objects.equals(signMsg, encrypt)) {
            log.error("签名错误");
            return;
        }
        String payResult = reqVo.getPayResult();
        String orderNo = reqVo.getOrderNo();
        if (Objects.equals("1", payResult)) {
            log.info("支付成功{}", orderNo);
            PaymentOrder paymentOrder = paymentOrderService.findLatestPaymentOrder(orderNo);
            paymentOrder.setLastNotifyTime(DateUtil.now());
            String chnltrxid = reqVo.getPaymentOrderId();
            confirmPaid(paymentOrder.getId(), chnltrxid);
        } else {
            log.info("支付失败{}", orderNo);
        }
    }

    /**
     * 通联支付结果回调
     *
     * @param reqVo 通知参数
     * @return if handled true ,otherwise null
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String handleAllInPayNotify(AllInPayPaymentNotifyReqVo reqVo) {
        // * 交易状态
        // * 0000：交易成功
        //* 3045：交易超时
        //* 3008：余额不足
        //* 3999：交易失败
        //* 2008：交易处理中
        //* 3050：交易已撤销
        String status = reqVo.getTrxstatus();
        AllInPayTrxStatusCode statusCode = AllInPayTrxStatusCode.valueOfCode(status);
        AssertUtils.notNull(statusCode, ILLEGAL_PARAMETER, "all-in-pay trxstatus is null or illegal.");
        log.info("支付结果{}", statusCode.toString());
        PaymentOrder paymentOrder = paymentOrderService.findLatestPaymentOrder(reqVo.getCusorderid());
        paymentOrder.setLastNotifyTime(DateUtil.now());
        String chnltrxid = reqVo.getChnltrxid();
        confirmPaid(paymentOrder.getId(), chnltrxid);
        allInPayPaymentLogService.createLog(paymentOrder.getId(), reqVo);
        switch (statusCode) {
            case SUCCESS:
                return SUCCESS;
            default:
                return null;
        }
    }

    private PaymentRefundRespVo buildRefundSuccessRespVo() {
        PaymentRefundRespVo paymentRefundRespVo;
        paymentRefundRespVo = new PaymentRefundRespVo();
        paymentRefundRespVo.setProcessSuccess(true);
        paymentRefundRespVo.setOutRefundNo(paymentRefundRespVo.getOutRefundNo());
        paymentRefundRespVo.setRefundApplySuccess(true);
        paymentRefundRespVo.setRefundSuccess(true);
        return paymentRefundRespVo;
    }

    private void updateRefundStatus(Long paymentOrderId, PaymentRefundRespVo paymentRefundRespVo) {
        if (paymentRefundRespVo.isProcessSuccess()) {
            if (paymentRefundRespVo.getRefundSuccess()) {
                paymentOrderService.markPaymentOrderPaymentStatusToRefunded(paymentOrderId, paymentRefundRespVo.getOutRefundNo(), new Timestamp(System.currentTimeMillis()));
            } else if (paymentRefundRespVo.getRefundApplySuccess()) {
                paymentOrderService.markPaymentOrderPaymentStatusToRefunding(paymentOrderId, paymentRefundRespVo.getOutRefundNo(), new Timestamp(System.currentTimeMillis()));
            }
        }
    }

    /**
     * 确认支付
     *
     * @param paymentOrderId 支付单号
     */
    private void confirmPaid(Long paymentOrderId, String tradeNo) {

        paymentOrderService.markPaymentOrderPaymentStatusToPaid(paymentOrderId, tradeNo, new Timestamp(System.currentTimeMillis()));
    }

    private void confirmRefunded(Long paymentOrderId, String tradeNo) {
        //TODO
    }

    /**
     * 直接从第三方查询支付状态
     *
     * @param paymentOrder 支付单
     * @return true if paid, otherwise false
     */
    private CommonPaidRespVo isPaid(PaymentOrder paymentOrder) {
        log.info("Query Payment order {} payment status", paymentOrder);
        PaymentChannelResVo paymentChannelInfo = paymentChannelService.getPaymentChannelInfo(Long.valueOf(paymentOrder.getMchId()), paymentOrder.getPaymentType(), paymentOrder.getPaymentTradeType());
        switch (paymentOrder.getPaymentType()) {
            case ALIPAY:
                AliOrderReqVo aliPaymentReqVo = JsonUtils.json2Obj(paymentChannelInfo.getParam(), AliOrderReqVo.class);
                return aliPaymentService.isPaid(aliPaymentReqVo, paymentOrder.getTradeNo(), paymentOrder.getMchOrderNo());
            case WECHAT:
                Properties properties = JsonUtils.json2Obj(paymentChannelInfo.getParam(), Properties.class);
                return wechatPaymentService.isPaid(properties, paymentOrder.getTradeNo(), paymentOrder.getMchOrderNo());
            default:
                throw new BizSilentException(ILLEGAL_PARAMETER, "PaymentType is null or illegal.");
        }
    }

    /**
     * 验证已存在的支付单的支付情况，并废弃可废弃的订单
     *
     * @param mchId      商户id
     * @param mchOrderNo 商户订单号
     * @throws PaymentOrderIsPaidException 如果已经有支付单完成付款
     */
    private void validateExistPaymentOrderPaymentStatusAndDiscardNotPaid(String mchId, String mchOrderNo) throws PaymentOrderIsPaidException {

        List<PaymentOrder> existPaymentOrders = paymentOrderService.findPaymentOrder(mchId, mchOrderNo);
        if (CollectionUtils.isNotEmpty(existPaymentOrders)) {
            log.info("Validate Payment order {} payment status, and discard them.", existPaymentOrders);
            Boolean needDiscardPayment = false;
            for (PaymentOrder existPaymentOrder : existPaymentOrders) {
                switch (existPaymentOrder.getPaymentStatus()) {
                    case DISCARD:
                        continue;
                    case CREATE_PAYMENT:
                        CommonPaidRespVo commonPaidRespVo = isPaid(existPaymentOrder);
                        if (commonPaidRespVo.isPaid()) {
                            confirmPaid(existPaymentOrder.getId(), commonPaidRespVo.getTradeNo());
                            throw new PaymentOrderIsPaidException(PaymentExceptionEnum.PAYMENT_ORDER_IS_PAID, format("Order was paid by PaymentOrder:[%s]", existPaymentOrder.getId()));
                        } else {
                            //TODO 调取第三方订单支付，设置为不可支付状态
                        }
                        needDiscardPayment = true;
                        break;
                    case PAID:
                        throw new PaymentOrderIsPaidException(PaymentExceptionEnum.PAYMENT_ORDER_IS_PAID, format("Order is paid by PaymentOrder:[%s]", existPaymentOrder.getId()));
                    case REFUNDED:
                        throw new PaymentOrderIsPaidException(PaymentExceptionEnum.PAYMENT_ORDER_IS_REFUNDED, format("Order is refunded by PaymentOrder:[%s]", existPaymentOrder.getId()));
                    default:
                        throw new BizSilentException(ILLEGAL_PARAMETER, format("Illegal PaymentStatus:[%s] of PaymentOrder:[%s]", existPaymentOrder.getPaymentStatus(), existPaymentOrder.getId()));
                }
            }
            if (needDiscardPayment) {
                paymentOrderService.discardAllPaymentOrderIfNotPaid(mchId, mchOrderNo);
            }
        }
    }

}
